package org.loong.crypto.extension.operator;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.util.BigIntegers;
import org.loong.crypto.common.exception.CryptoException;
import org.loong.crypto.core.utils.ECKeyUtils;
import org.loong.crypto.model.asn1.sm2.SM2Cipher;

public class SM2Utils {

    public static final int SM3_DIGEST_LENGTH = 32;

    /**
     * Convert SM2Cipher.
     * 
     * @param encryptedData the encryptedData
     * @return the {@link SM2Cipher}
     */
    public static SM2Cipher convertSM2Cipher(byte[] encryptedData) {
        X9ECParameters x9ECParameters = GMNamedCurves.getByName(ECKeyUtils.SM2_CURVE_NAME);
        ECCurve curve = x9ECParameters.getCurve();
        int curveLength = (curve.getFieldSize() + 7) / 8;

        byte[] c1 = new byte[curveLength * 2 + 1];
        System.arraycopy(encryptedData, 0, c1, 0, c1.length);

        byte[] x = new byte[32];
        System.arraycopy(c1, 1, x, 0, x.length);

        byte[] y = new byte[32];
        System.arraycopy(c1, 1 + x.length, y, 0, y.length);
        
        byte[] c3 = new byte[SM3_DIGEST_LENGTH];
        System.arraycopy(encryptedData, c1.length, c3, 0, c3.length);

        byte[] c2 = new byte[encryptedData.length - c1.length - SM3_DIGEST_LENGTH];
        System.arraycopy(encryptedData, c1.length + SM3_DIGEST_LENGTH, c2, 0, c2.length);
        return new SM2Cipher(x, y, c3, c2);
    }

    /**
     * Reduce CipherText.
     * 
     * @param sm2Cipher the sm2Cipher
     * @return the sm2 cipherText
     */
    public static byte[] reduceCipherText(SM2Cipher sm2Cipher) {
        X9ECParameters x9ECParameters = GMNamedCurves.getByName(ECKeyUtils.SM2_CURVE_NAME);
        ECCurve curve = x9ECParameters.getCurve();
        int curveLength = (curve.getFieldSize() + 7) / 8;

        try {
            byte[] x = BigIntegers.asUnsignedByteArray(sm2Cipher.getX().getValue());
            byte[] y = BigIntegers.asUnsignedByteArray(sm2Cipher.getY().getValue());

            byte[] c1 = new byte[curveLength * 2 + 1];
            c1[0] = 0x04;

            System.arraycopy(x, 0, c1, 1, x.length);
            System.arraycopy(y, 0, c1, x.length + 1, y.length);

            byte[] hash = sm2Cipher.getHash().getOctets();
            byte[] c3 = new byte[SM3_DIGEST_LENGTH];
            System.arraycopy(hash, 0, c3, 0, hash.length);

            byte[] cipherText = sm2Cipher.getCipherText().getOctets();
            byte[] c2 = new byte[cipherText.length];
            System.arraycopy(cipherText, 0, c2, 0, cipherText.length);

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            out.write(c1);
            out.write(c3);
            out.write(c2);
            return out.toByteArray();
        } catch (IOException e) {
            throw new CryptoException(e.getMessage(), e);
        }
    }
}
